package wiki.hitime.stl.daq.service.task;

import wiki.hitime.stl.daq.bean.bo.DataAcquisitionValueBO;
import wiki.hitime.stl.daq.bean.bo.DataCheckResultBO;
import wiki.hitime.stl.daq.bean.bo.DataCheckValueBO;
import wiki.hitime.stl.daq.bean.db.DaqAcquisitionRule;
import wiki.hitime.stl.daq.bean.db.DaqCheckRule;
import wiki.hitime.stl.daq.manager.DaqAcquisitionDataRecordManager;
import wiki.hitime.stl.daq.manager.DaqAcquisitionRuleManager;
import wiki.hitime.stl.daq.manager.DaqCheckRecordManager;
import wiki.hitime.stl.daq.manager.DaqCheckRuleManager;
import wiki.hitime.stl.daq.service.factory.DataCheckFactory;
import wiki.hitime.stl.daq.support.ContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @author zhangdaoping
 * @create 2019-07-09 18:57
 */
@Service
@Slf4j
public class DataCheckService {

    private DaqCheckRuleManager daqCheckRuleManager;
    private DaqCheckRecordManager daqCheckRecordManager;
    private DaqAcquisitionDataRecordManager daqAcquisitionDataRecordManager;
    private DataMsgService dataMsgService;
    private DaqAcquisitionRuleManager daqAcquisitionRuleManager;

    public DataCheckService(DaqCheckRuleManager daqCheckRuleManager,
                            DaqCheckRecordManager daqCheckRecordManager,
                            DaqAcquisitionDataRecordManager daqAcquisitionDataRecordManager,
                            DataMsgService dataMsgService,
                            DaqAcquisitionRuleManager daqAcquisitionRuleManager) {
        this.daqCheckRuleManager = daqCheckRuleManager;
        this.daqCheckRecordManager = daqCheckRecordManager;
        this.daqAcquisitionDataRecordManager = daqAcquisitionDataRecordManager;
        this.dataMsgService = dataMsgService;
        this.daqAcquisitionRuleManager = daqAcquisitionRuleManager;
    }

    /**
     * 检查数据
     *
     * @param executeRecordId     执行记录编号
     * @param acquisitionRuleId   采集规则编号
     * @param acquisitionRecordId 采集记录
     */
    public void checkData(Long executeRecordId, Long acquisitionRuleId, Long acquisitionRecordId) {
        // 根据数据采集规则id获取检查规则集合
        List<DaqCheckRule> checkRuleList = daqCheckRuleManager.loadRuleList(acquisitionRuleId);
        if (CollectionUtils.isEmpty(checkRuleList)) {
            return;
        }
        int checkProcessSucceed = checkRuleList.stream().mapToInt(checkRule -> {
            // 添加数据检查记录
            Long checkRecordId = daqCheckRecordManager.addNotProcessRecord(executeRecordId, checkRule, acquisitionRecordId);
            try {
                // 根据检查规则集合和数据记录进行数据检查
                DataCheckResultBO resultBO = this.loadCheckResult(acquisitionRuleId, acquisitionRecordId, checkRule);
                // 更新数据检查状态
                daqCheckRecordManager.updateProcessSucceed(checkRecordId, resultBO.getCheckResult(), resultBO.getCheckSummary());
                // 根据检查是否通过进行消息通知
                if (resultBO.getCheckResult()) {
                    dataMsgService.notifyMsg(executeRecordId, checkRule.getId(), checkRecordId);
                }
                return 1;
            } catch (Exception e) {
                log.error("检查数据出现异常，executeRecordId={}", executeRecordId, e);
                // 更新数据检查状态
                daqCheckRecordManager.updateProcessFailed(checkRecordId, e.getMessage());
                return 0;
            }
        }).sum();
    }

    /**
     * 加载检查结果
     *
     * @param acquisitionRecordId 数据采集记录id
     * @param checkRule           检查规则
     * @return
     */
    private DataCheckResultBO loadCheckResult(Long acquisitionRuleId, Long acquisitionRecordId, DaqCheckRule checkRule) {
        DataCheckFactory checkFactory = ContextHolder.getBean(checkRule.getImplServiceName());
        Assert.notNull(checkFactory, "检查实现类为空");
        List<String[]> dataList = daqAcquisitionDataRecordManager.loadDataRecord(acquisitionRecordId);
        DaqAcquisitionRule acquisitionRule = daqAcquisitionRuleManager.getById(acquisitionRuleId);
        DataAcquisitionValueBO dataAcquisitionValueBO = DataAcquisitionValueBO.builder()
                .dataList(dataList)
                .valueType(acquisitionRule.getValueType())
                .valueFormat(acquisitionRule.getValueFormat())
                .build();
        DataCheckValueBO dataCheckValueBO = DataCheckValueBO.builder()
                .dataList(Arrays.asList(checkRule.getExpectCheckData().split(",")))
                .build();
        return checkFactory.loadCheckResult(dataAcquisitionValueBO, dataCheckValueBO);
    }
}
